/*
 * Copyright 2012-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.codestd.security.shiro.filter;

import com.codestd.security.mapper.SysResourceMapper;
import com.codestd.security.model.SysResource;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 自定义ShiroFilterFactoryBean，从数据库中动态获取配置
 *
 * @author Wang Chengwei(Jaune)
 * @since 1.0.0
 */
public class DynamicShiroFilterFactoryBean extends ShiroFilterFactoryBean implements ChainDefinitionRefreshable {

    private static Logger logger = LoggerFactory.getLogger(DynamicShiroFilterFactoryBean.class);
    // 不能使用@Autowired，会出现无法注入的问题。
    private SysResourceMapper sysResourceMapper;

    private Map<String, String> manualFilterChainDefinitionMap;

    public void setSysResourceMapper(SysResourceMapper sysResourceMapper) {
        this.sysResourceMapper = sysResourceMapper;
    }

    @Override
    public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
        // 刷新资源时使用
        this.manualFilterChainDefinitionMap = filterChainDefinitionMap;
        this.setFilterChainDefinitionMap();
    }
    
    private void setFilterChainDefinitionMap() {
        
        Map<String, String> mergedFilterChainDefinitionMap = new LinkedHashMap<>();
        Map<String, FilterChainDefinitionMap> databaseFilterChainDefinitionMap = new LinkedHashMap<>();

        List<SysResource> resourcePermissions = this.sysResourceMapper.findResourcePermissionMap();
        for (SysResource resourcePermission : resourcePermissions) {
            FilterChainDefinitionMap filterChainDefinitionMap =
                    databaseFilterChainDefinitionMap.computeIfAbsent(resourcePermission.getResourceUrl(), o -> new FilterChainDefinitionMap());
            filterChainDefinitionMap.addPermission(resourcePermission.getPermissionMark());
        }

        List<SysResource> resourceRoles = this.sysResourceMapper.findResourceRoleMap();
        for (SysResource resourceRole : resourceRoles) {
            FilterChainDefinitionMap filterChainDefinitionMap =
                    databaseFilterChainDefinitionMap.computeIfAbsent(resourceRole.getResourceUrl(), o -> new FilterChainDefinitionMap());
            filterChainDefinitionMap.addRole(resourceRole.getRoleMark());
        }

        for (Map.Entry<String, FilterChainDefinitionMap> entry : databaseFilterChainDefinitionMap.entrySet()) {
            mergedFilterChainDefinitionMap.put(entry.getKey(), entry.getValue().getChainDefinition());
        }

        // 添加配置文件中配置的filterChainDefinitionMap
        for (Map.Entry<String, String> entry : this.manualFilterChainDefinitionMap.entrySet()) {
            mergedFilterChainDefinitionMap.put(entry.getKey(), entry.getValue());
        }
        
        super.setFilterChainDefinitionMap(mergedFilterChainDefinitionMap);
    }

    @Override
    public void refresh() throws Exception {
        // 重新设置FilterChainDefinitionMap
        this.setFilterChainDefinitionMap();
        // FilterChainDefinitionMap中的内容已经发生变更，所以此时创建的FilterChainManager中是变更后的数据
        FilterChainManager manager = createFilterChainManager();
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        AbstractShiroFilter shiroFilter = (AbstractShiroFilter) this.getObject();
        if (shiroFilter != null) {
            shiroFilter.setFilterChainResolver(chainResolver);
        }
        logger.info("资源权限刷新成功。");
    }

    // 将权限拼接成auth[PERM_perm001,ROLE_admin]的形式
    static class FilterChainDefinitionMap {
        private Set<String> auths = new HashSet<>();

        void addPermission(String permission) {
            String newPermission = RolePermissionAuthorizationFilter.DEFAULT_PERMISSION_PREFIX + permission;
            this.auths.add(newPermission);
        }
        void addRole(String role) {
            String newRole = RolePermissionAuthorizationFilter.DEFAULT_ROLE_PREFIX + role;
            this.auths.add(newRole);
        }

        String getChainDefinition() {
            if (this.auths.isEmpty()) {
                return null;
            }
            return "auth[" + StringUtils.arrayToDelimitedString(this.auths.toArray(), ",") + "]";
        }
    }
    /*static class FilterChainDefinitionMap {
        private Set<String> permissions = new HashSet<>();

        void addPermission(String permission) {
            if (!this.permissions.contains(permission)) {
                this.permissions.add(permission);
            }
        }

        String getChainDefinition() {
            StringBuilder sb = new StringBuilder();
            if (this.permissions.size() > 0) {
                sb.append("perms[")
                        .append(StringUtils.arrayToDelimitedString(this.permissions.toArray(), ","))
                        .append("]");
            }

            return sb.toString();
        }
    }*/
}
